using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Refit;
using StackExchange.Redis;

namespace WaferService
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            builder.Services.AddSingleton<IEFEMService, EFEMService>();
            builder.Services.AddSingleton<IPLCService, PLCService>();
            builder.Services.AddSingleton<IMotionControlService, MotionControlService>();
            builder.Services.AddSingleton<WaferTransferService>();
            builder.Services.AddSingleton<SubstrateTrackingService>();

            builder.Services.AddDbContextFactory<ApplicationDbContext>(options => options
                .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
                .UseSqlite(builder.Configuration.GetConnectionString("Database")));

            builder.Services.AddSingleton(_ =>
                ConnectionMultiplexer.Connect(builder.Configuration.GetConnectionString("RedisConfiguration")!));

            builder.Services.AddTransient<LoadingPositionShim>();

            builder.Services.AddTransient<FoupService>();
            builder.Services.AddTransient(_ => ConfigValue.Create(new SamplePositionConfig()));

            builder.Services.AddRefitClient<IEChuckServiceClient>()
                .ConfigureHttpClient(c =>
                {
                    c.BaseAddress = new Uri("http://127.0.0.1:56663/");
                    c.Timeout = TimeSpan.FromSeconds(10);
                });
            builder.Services.AddRefitClient<IHVServiceClient>()
                .ConfigureHttpClient(c =>
                {
                    c.BaseAddress = new Uri("http://127.0.0.1:56662/");
                    c.Timeout = TimeSpan.FromSeconds(10);
                });

            builder.Services.Configure<EFEMConfig>(builder.Configuration.GetSection("EFEMConfig"));
            builder.Services.Configure<PLCConfig>(builder.Configuration.GetSection("PLCConfig"));
            builder.Services.Configure<MotionControlConfig>(builder.Configuration.GetSection("MotionControlConfig"));
            builder.Services.AddTransient(p => p.GetRequiredService<IOptions<MotionControlConfig>>().Value);

            builder.Services.AddExceptionHandler<GlobalExceptionHandler>();

            builder.Configuration.AddJsonFile("D:\\systemconfig\\WaferServiceConfig.json", optional: true);
            builder.Configuration.AddEnvironmentVariables();
            builder.Configuration.AddCommandLine(args);

            builder.AddInstrumentation(builder.Environment.ApplicationName, "1.0.0");

            var app = builder.Build();
            app.UseExceptionHandler(_ => { });

            app.MapPost("/reset", static async (WaferTransferService service) =>
            {
                await service.Reset();
            });

            app.MapGet("/aligner_angle", static async (WaferTransferService service) =>
            {
                var angle = await service.GetAlignerAngle();
                return new AlignerAngleResponse
                {
                    Angle = angle
                };
            });

            app.MapPost("/aligner_angle", static async ([FromBody] AlignerAngleRequest req, WaferTransferService service) =>
            {
                await service.SetAlignerAngle(req.Angle);
            });

            app.MapPost("/track", static async (SubstrateTrackingService service) =>
            {
                var result = await service.Track();
                return Results.Ok(result);
            });

            app.MapPost("/open_foup/{foup}", static (int foup, FoupService service) =>
            {
                var id = service.Open(foup);
                return Results.Ok(new WaferServiceResponse
                {
                    Id = id
                });
            });

            app.MapPost("/close_foup/{foup}", static (int foup, FoupService service) =>
            {
                var id = service.Close(foup);
                return Results.Ok(new WaferServiceResponse
                {
                    Id = id
                });
            });

            app.MapPost("/load_wafer", static async ([FromBody] WaferTransferRequest req, WaferTransferService service) =>
            {
                var id = await service.Load(req);
                return Results.Ok(new WaferServiceResponse
                {
                    Id = id
                });
            });

            app.MapPost("/unload_wafer", static async ([FromBody] WaferTransferRequest req, WaferTransferService service) =>
            {
                var id = await service.Unload(req);
                return Results.Ok(new WaferServiceResponse
                {
                    Id = id
                });
            });

            app.MapPost("/queue", static async ([FromBody] WaferLoadingQueueRequest req, WaferTransferService service) =>
            {
                var id = await service.LoadAll(req);
                return Results.Ok(new WaferServiceResponse
                {
                    Id = id
                });
            });

            app.MapPost("/queue/{id}/unload", static async (Guid id, WaferTransferService service, CancellationToken tok) =>
            {
                var q = await service.GetLoadingQueue(id);
                await q.Unload(tok);
            });

            app.MapPost("/queue/{id}/health_check", static async (Guid id, WaferTransferService service, CancellationToken tok) =>
            {
                var q = await service.GetLoadingQueue(id);
                var ok = await q.HealthCheck(tok);
                if (ok)
                {
                    return Results.Ok();
                }
                else
                {
                    return Results.NotFound();
                }
            });

            app.MapPost("/queue/{id}/request_completion", static async (Guid id, WaferTransferService service) =>
            {
                var q = await service.GetLoadingQueue(id);
                q.RequestCompletion();
                return Results.Ok();
            });

            app.MapPost("/queue/{id}/cancel", static async (Guid id, WaferTransferService service) =>
            {
                var q = await service.GetLoadingQueue(id);
                q.Cancel();
                return Results.Ok();
            });

            var factory = app.Services.GetRequiredService<IDbContextFactory<ApplicationDbContext>>();
            EnsureDatabaseCreated(factory);

            app.Run();
        }

        // FIXME
        static void EnsureDatabaseCreated(IDbContextFactory<ApplicationDbContext> factory)
        {
            using var ctx = factory.CreateDbContext();
            ctx.Database.EnsureCreated();
        }
    }
}